﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using codeding.KMeans.DataStructures.Lists;
//using Microsoft.Office.Interop.Excel;
using System.Data.SqlClient;
using System.Data;

namespace codeding.KMeans.DataStructures
{
    public class BuildMatrixDoTuongTu
    {
        private int _SoHang;
        private int _SoCot;
        private DataTable _DtRatings = null;
        private DanhMucUserAccount _DmUserAccount = DanhMucUserAccount.Instance;
        private int _UserSignInID = -1;
        public int[] _ListUserClustered;
        public BuildMatrixUsersItemsBase _MatrixUsersItemsBase;
        public double[,] _MaTran;

        //Dùng để phân cụm
        PointCollection _Points = new PointCollection();

        //public BuildMatrixDoTuongTu()
        //{
        //    try
        //    {
        //        //KHởi tạo ma trận user item là đầu vào để tính sim
        //        _MatrixUsersItemsBase = new BuildMatrixUsersItemsBase();

        //        _SoHang = _DmUserAccount.DataView.Count + 1;
        //        _SoCot = _DmUserAccount.DataView.Count + 1;

        //        InitMatran(_SoHang, _SoCot);
        //        FillDataMaTran();

        //        //Xuất file
        //        RWFile.ExportToTextFile(_MaTran, _SoHang, _SoCot, "MatrixDoTuongTu");
        //    }
        //    catch (Exception ex)
        //    {
        //        throw new Exception(ex.Message);
        //    }
        //    finally
        //    {
        //    }
        //}
        public BuildMatrixDoTuongTu(int userSignInID, bool readFromFile)
        {
            try
            {
                _UserSignInID = userSignInID;
                //KHởi tạo ma trận user item là đầu vào để tính sim
                _MatrixUsersItemsBase = new BuildMatrixUsersItemsBase(readFromFile);

                _SoHang = _DmUserAccount.DataView.Count + 1;
                _SoCot = _DmUserAccount.DataView.Count + 1;

                if (!readFromFile)
                {
                    InitMatran(_SoHang, _SoCot);
                    FillDataMaTran();
                    //Xuất file
                    RWFile.ExportToTextFile(_MaTran, _SoHang, _SoCot, "MatrixDoTuongTu");
                }
                else
                {
                    _MaTran = RWFile.ReadFromTextFile("MatrixDoTuongTu");
                }
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
            finally
            {
            }
        }

        //Khởi tạo ma trận với số hàng = số cột = số user trong danh mục.
        private void InitMatran(int soHang, int soCot)
        {
            this._SoHang = soHang;
            this._SoCot = soCot;
            this._MaTran = new double[_SoHang, _SoCot];

            for (int i = 0; i < _SoHang; i++)
            {
                for (int j = 0; j < _SoCot; j++)
                {
                    _MaTran[i, j] = 0;
                }
            }
        }

        //ĐIền thông tin Sim tính được vào ma trận. Đây là ma trận vuông và chỉ có nửa trên ma trận có giá trị
        private void FillDataMaTran()
        {
            try
            {
                for (int i = 0; i < _SoHang; i++)
                {
                    for (int k = i + 1; k < _SoCot; k++)
                    {
                        _MaTran[i, k] = CalculateSimByUserID(i, k);
                    }
                    if (i > 0 && i % 50 == 0)
                    {
                        //Xuất file
                        RWFile.ExportToTextFile(_MaTran, _SoHang, _SoCot, "MatrixDoTuongTu");
                    }
                    listUserClustered();
                }
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }

        //
        private void listUserClustered()
        {
            //lấy độ tương tự của user đăng nhập ra
            for (int i = 1; i < _SoCot; i++)
            {
                _Points.Add(new Point(i.ToString(), _MaTran[_UserSignInID, i]));
            }
            //Phân cụm sao cho mỗi cụm có 10 user
            List<PointCollection> allClusters = KMeans.DoKMeans(_Points, DanhMucUserAccount.Instance.Count / 10);

            //Tìm nhóm có tâm cụm lớn nhất, user cần xét sẽ nằm trong đó. vì độ tương tự của chính là = vô cùng
            PointCollection cluster = FindClusterMaxCentroid(allClusters);

            _ListUserClustered = new int[cluster.Count];
            int index = 0;

            foreach (Point p in cluster)
            {
                _ListUserClustered[index++] = Convert.ToInt32(p.UserId);
            }
        }

        private PointCollection FindClusterMaxCentroid(List<PointCollection> allClusters)
        {
            double max = -1000;
            PointCollection clusterNeedFinding = null;
            foreach (PointCollection pointCollection in allClusters)
            {
                if (max < Math.Abs(pointCollection.Centroid.Sim))
                {
                    max = Math.Abs(pointCollection.Centroid.Sim);
                    clusterNeedFinding = pointCollection;
                }
            }
            return clusterNeedFinding;
        }

        //Tính toán sim
        private double CalculateSimByUserID(int userIDi, int userIDk)
        {
            int[] l = DanhMucUserAccount.GetMovieIDDanhGiaBoiCa2User(userIDi, userIDk);
            double ri = DanhMucUserAccount.DanhGiaTrungBinhByUser(userIDi);
            double rk = DanhMucUserAccount.DanhGiaTrungBinhByUser(userIDk);

            double tu = 0, mau1 = 0, mau2 = 0;
            double sim = 0;
            for (int j = 0; j < l.Length; j++)
            {
                tu += (_MatrixUsersItemsBase._MaTran[userIDi, l[j]] - ri) *
                        (_MatrixUsersItemsBase._MaTran[userIDk, l[j]] - rk);

                mau1 += ((_MatrixUsersItemsBase._MaTran[userIDi, l[j]] - ri) *
                        (_MatrixUsersItemsBase._MaTran[userIDi, l[j]] - ri));

                mau2 += ((_MatrixUsersItemsBase._MaTran[userIDk, l[j]] - rk) *
                        (_MatrixUsersItemsBase._MaTran[userIDk, l[j]] - rk));
            }
            if (mau1 + mau2 != 0)
                sim = Convert.ToDouble(tu) / Math.Sqrt(Convert.ToDouble(mau1 * mau2));
            return sim;
        }
    }
}
